using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace IppServer.Host.Discovery
{
    /// <summary>
    /// 原生mDNS协议发布器
    /// 使用UDP多播发送mDNS服务公告
    /// </summary>
    internal sealed class NativeMdnsPublisher : IDisposable
    {
        private const int MdnsPort = 5353;
        private static readonly IPAddress MdnsMulticastAddress = IPAddress.Parse("224.0.0.251");
        private static readonly IPEndPoint MdnsEndpoint = new IPEndPoint(MdnsMulticastAddress, MdnsPort);
        
        private UdpClient _udpClient;
        private Timer _announceTimer;
        private bool _isDisposed;
        private readonly string _serviceName;
        private readonly string _fullServiceName;
        private readonly int _port;
        private readonly string _txtRecord;

        public NativeMdnsPublisher(string serviceName, string printerName, string queueName, int port, string txtRecord = null)
        {
            _serviceName = serviceName;
            _fullServiceName = $"{serviceName}._ipp._tcp.local";
            _port = port;
            
            // 如果提供了完整的TXT记录，使用它；否则使用简化的格式
            _txtRecord = !string.IsNullOrEmpty(txtRecord) 
                ? txtRecord 
                : $"rp=printers/{queueName} ty={printerName}";
        }

        /// <summary>
        /// 启动mDNS服务公告
        /// </summary>
        public bool Start()
        {
            try
            {
                // 创建UDP客户端
                _udpClient = new UdpClient();
                _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                
                // 绑定到任意本地地址和5353端口
                _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, MdnsPort));
                
                // 加入多播组
                _udpClient.JoinMulticastGroup(MdnsMulticastAddress);
                
                // 设置多播TTL
                _udpClient.Ttl = 255;
                
                // 设置多播接口（任意接口）
                _udpClient.MulticastLoopback = true;
                
                // 立即发送一次公告
                AnnounceService();
                
                // 设置定时器，每60秒发送一次公告（mDNS标准）
                _announceTimer = new Timer(OnAnnounceTimer, null, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
                
                return true;
            }
            catch
            {
                // 如果绑定失败（可能是端口被占用或权限不足），返回false
                Dispose();
                return false;
            }
        }

        /// <summary>
        /// 停止服务公告
        /// </summary>
        public void Stop()
        {
            try
            {
                // 发送"goodbye"消息（TTL=0）
                SendGoodbyeMessage();
            }
            catch
            {
                // 忽略错误
            }
            
            Dispose();
        }

        /// <summary>
        /// 发送服务公告
        /// </summary>
        private void AnnounceService()
        {
            try
            {
                // 构建DNS响应消息
                var dnsMessage = BuildDnsAnnouncementMessage();
                if (dnsMessage != null && dnsMessage.Length > 0)
                {
                    _udpClient?.Send(dnsMessage, dnsMessage.Length, MdnsEndpoint);
                }
            }
            catch
            {
                // 忽略发送错误
            }
        }

        /// <summary>
        /// 构建DNS公告消息
        /// </summary>
        private byte[] BuildDnsAnnouncementMessage()
        {
            try
            {
                // DNS消息格式简化实现
                // 实际完整的DNS/mDNS实现需要处理：
                // - DNS报文头（12字节）
                // - 问题部分
                // - 答案部分（包含PTR、SRV、TXT记录）
                // - 授权部分
                // - 附加部分
                
                // 这里实现一个简化的mDNS公告消息
                var message = new List<byte>();
                
                // DNS报文头（12字节）
                var transactionId = (ushort)(new Random().Next(0, 65535));
                message.AddRange(BitConverter.GetBytes((ushort)(transactionId | 0x8400))); // ID + Flags (标准查询响应)
                message.AddRange(BitConverter.GetBytes((ushort)0)); // Questions count
                message.AddRange(BitConverter.GetBytes((ushort)1)); // Answer RRs count
                message.AddRange(BitConverter.GetBytes((ushort)0)); // Authority RRs count
                message.AddRange(BitConverter.GetBytes((ushort)2)); // Additional RRs count (SRV + TXT)
                
                // 答案部分：PTR记录指向服务实例
                // 名称：_ipp._tcp.local
                var serviceType = "_ipp._tcp.local";
                message.AddRange(EncodeDnsName(serviceType));
                
                // PTR记录
                message.AddRange(BitConverter.GetBytes((ushort)12)); // TYPE: PTR (12)
                message.AddRange(BitConverter.GetBytes((ushort)1)); // CLASS: IN (1)
                message.AddRange(BitConverter.GetBytes((uint)4500)); // TTL: 75分钟
                message.AddRange(BitConverter.GetBytes((ushort)_fullServiceName.Length + 2)); // RDLENGTH
                message.AddRange(EncodeDnsName(_fullServiceName)); // RDATA: 服务实例名称
                
                // 附加记录1：SRV记录
                var srvRecord = BuildSrvRecord();
                message.AddRange(srvRecord);
                
                // 附加记录2：TXT记录
                var txtRecord = BuildTxtRecord();
                message.AddRange(txtRecord);
                
                return message.ToArray();
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 构建SRV记录
        /// </summary>
        private byte[] BuildSrvRecord()
        {
            var record = new List<byte>();
            
            // 名称（指向服务实例，使用压缩指针）
            record.Add((byte)0xC0); // 压缩指针标记
            record.Add((byte)0x0C); // 指向PTR记录中的名称位置（偏移12）
            
            // TYPE: SRV (33)
            record.AddRange(BitConverter.GetBytes((ushort)33));
            
            // CLASS: IN (1) + 缓存刷新标志
            record.AddRange(BitConverter.GetBytes((ushort)0x8001));
            
            // TTL: 120秒（2分钟）
            record.AddRange(BitConverter.GetBytes((uint)120));
            
            // RDATA
            record.AddRange(BitConverter.GetBytes((ushort)0)); // Priority
            record.AddRange(BitConverter.GetBytes((ushort)0)); // Weight
            record.AddRange(BitConverter.GetBytes((ushort)_port)); // Port
            
            // Target: 主机名（本机主机名）
            var hostname = Dns.GetHostName();
            record.AddRange(BitConverter.GetBytes((ushort)(hostname.Length + 6))); // RDLENGTH
            record.AddRange(EncodeDnsName(hostname + ".local"));
            
            return record.ToArray();
        }

        /// <summary>
        /// 构建TXT记录
        /// </summary>
        private byte[] BuildTxtRecord()
        {
            var record = new List<byte>();
            
            // 名称（指向服务实例，使用压缩指针）
            record.Add((byte)0xC0);
            record.Add((byte)0x0C);
            
            // TYPE: TXT (16)
            record.AddRange(BitConverter.GetBytes((ushort)16));
            
            // CLASS: IN (1) + 缓存刷新标志
            record.AddRange(BitConverter.GetBytes((ushort)0x8001));
            
            // TTL: 4500秒
            record.AddRange(BitConverter.GetBytes((uint)4500));
            
            // RDATA: TXT记录包含多个字符串，每个字符串前面有长度字节
            // 格式：每个 "key=value" 对作为一个单独的字符串
            var txtParts = _txtRecord.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            var txtData = new List<byte>();
            
            foreach (var part in txtParts)
            {
                if (string.IsNullOrEmpty(part))
                    continue;
                    
                var partBytes = Encoding.UTF8.GetBytes(part);
                if (partBytes.Length > 255)
                {
                    // 如果单个键值对超过255字节，截断（不应该发生）
                    partBytes = new byte[255];
                }
                txtData.Add((byte)partBytes.Length);
                txtData.AddRange(partBytes);
            }
            
            // 如果没有任何TXT数据，至少添加一个空字符串
            if (txtData.Count == 0)
            {
                txtData.Add(0); // 空字符串
            }
            
            // 添加RDLENGTH（总长度）
            record.AddRange(BitConverter.GetBytes((ushort)txtData.Count));
            record.AddRange(txtData);
            
            return record.ToArray();
        }

        /// <summary>
        /// 编码DNS名称（将"example.com"编码为"7example3com0"）
        /// </summary>
        private byte[] EncodeDnsName(string name)
        {
            var parts = name.Split('.');
            var encoded = new List<byte>();
            
            foreach (var part in parts)
            {
                if (string.IsNullOrEmpty(part))
                    continue;
                    
                encoded.Add((byte)part.Length);
                encoded.AddRange(Encoding.UTF8.GetBytes(part));
            }
            
            encoded.Add(0); // 结束标记
            
            return encoded.ToArray();
        }

        /// <summary>
        /// 发送"goodbye"消息（TTL=0，表示服务停止）
        /// </summary>
        private void SendGoodbyeMessage()
        {
            try
            {
                var message = BuildDnsAnnouncementMessage();
                if (message != null && message.Length > 0)
                {
                    // 将TTL设置为0（在答案记录的TTL字段中）
                    // 这里简化处理，发送一个空消息即可
                    // 完整的实现应该修改TTL字段为0
                    _udpClient?.Send(new byte[0], 0, MdnsEndpoint);
                }
            }
            catch
            {
                // 忽略错误
            }
        }

        /// <summary>
        /// 定时器回调
        /// </summary>
        private void OnAnnounceTimer(object state)
        {
            if (!_isDisposed)
            {
                AnnounceService();
            }
        }

        public void Dispose()
        {
            if (_isDisposed)
            {
                return;
            }

            _isDisposed = true;
            
            _announceTimer?.Dispose();
            _announceTimer = null;
            
            try
            {
                _udpClient?.DropMulticastGroup(MdnsMulticastAddress);
            }
            catch
            {
                // 忽略错误
            }
            
            try
            {
                _udpClient?.Close();
            }
            catch
            {
                // 忽略错误
            }
            
            _udpClient?.Dispose();
            _udpClient = null;
        }
    }
}

